﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Resources;
using System.Collections;
using Lime49;
using LockCrypt.Core;
using LockCrypt.DAL;

namespace LockCrypt.Mobile {
    /// <summary>
    /// A form which displays input fields to edit an account
    /// </summary>
    public partial class FrmEditAccount : Form {
        /*private Hashtable textBoxMap = new Hashtable(),
                          hiddenMap = new Hashtable();*/
        private Dictionary<string, TextBox> textBoxMap = new Dictionary<string, TextBox>();
        private Dictionary<string, string> hiddenMap = new Dictionary<string, string>();
        private Size btnOptionsSize;
        private TextBox txtNotes;
        private IList<IGroup> groups;
        private IEnumerable<ITemplate> templates;
        private BindingSource bsGroups,
                              bsTemplates;
        private bool cancelDialog = false,
                     editing = false;
        //private AccountType removedType = null;

        #region Appearance Constants
        private Font fieldNameFont = WMConstants.FieldNameFont,
                     fieldValueFont = WMConstants.FieldValueFont;
        private int fontHeight,
                    fieldSpacingY,
                    btnOptionsX, // X co-ordinate of the '>' button
                    labelWidth; // width of the labels to use to show account field names
        #endregion

        /// <summary>
        /// Gets or sets the account being added/edited.
        /// </summary>
        /// <value>The account.</value>
        public IAccount Account { get; set; }

        /// <summary>
        /// Adds listeners and sets up font sizes.
        /// </summary>
        private FrmEditAccount() {
            InitializeComponent();
            Account = new AccountBase();
            lblName.Font = fieldNameFont;
            lblGroup.Font = fieldNameFont;
            lblType.Font = fieldNameFont;
            fieldSpacingY = lblGroup.Top - (lblName.Top + lblName.Height);
            btnOptionsSize = new Size(24, txtName.Height);
            labelWidth = lblName.Width - btnOptionsSize.Width - 1;
            btnOptionsX = lblName.Left + labelWidth + 1;
            itmSave.Click += (s, e) => {
                if(ValidateAccount()) {
                    SaveFields();
                    this.DialogResult = DialogResult.OK;
                }
            };
            itmCancel.Click += (s, e) => {
                cancelDialog = true;
                this.DialogResult = DialogResult.Cancel;
            };
            this.Closing += delegate(object sender, CancelEventArgs e) {
                if(!cancelDialog) {
                    if(ValidateAccount()) {
                        this.DialogResult = DialogResult.OK;
                    } else {
                        e.Cancel = true;
                    }
                }
            };
            itmAddField.Click += (s, e) => {
                using(FrmEditField frmEditField = new FrmEditField(this, Account.Fields)) {
                    if(frmEditField.ShowDialog() == DialogResult.OK) {
                        IField newField = new FieldBase() {
                            Name = frmEditField.FieldName,
                            SortIndex = Account.Fields.Count
                        };
                        Account.Fields.Add(newField);
                        ShowFields();
                    }
                }
            };
            groups = DalConfigurationManager.Provider.GetGroups();
            groups.Insert(0, new GroupBase() {
                Name = I18nUtils.GetString("Strings", "None"),
                ID = null,
                ParentID = null
            });
            bsGroups = new BindingSource() { DataMember = "ID" };
            foreach(IGroup grp in groups) {
                bsGroups.Add(grp);
            }
            ddGroup.DisplayMember = "Name";
            ddGroup.ValueMember = "ID";
            ddGroup.DataSource = bsGroups;
            ddGroup.SelectedIndex = 0;
            ddGroup.ValueMember = "ID";
            templates = DalConfigurationManager.Provider.GetTemplates();
            bsTemplates = new BindingSource() { DataMember = "ID" };
            foreach(ITemplate template in templates) {
                bsTemplates.Add(template);
            }
            ddType.DisplayMember = "Name";
            ddType.ValueMember = "ID";
            ddType.DataSource = bsTemplates;
            //ShowFields();
            /*if(settings.Types.Count == 0) {
                MessageBox.Show(lang.GetString("Err_NoTypesDesc"), lang.GetString("Err_NoTypes"), MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
             * Close()?
            } */
        }

        /// <summary>
        /// Initializes a new <see cref="FrmEditAccount"/> to add an account.
        /// </summary>
        /// <param name="parentId">The initial value of the 'Group' combobox.</param>
        public FrmEditAccount(string parentId)
            : this() {
            this.Text = I18nUtils.GetString("Strings", "AddAccount");
            itmSave.Text = I18nUtils.GetString("Strings", "Add");
            string blankName = I18nUtils.GetString("Strings", "Blank");
            ITemplate blankTemplate = templates.FirstOrDefault(g => g.Name == blankName) ?? new TemplateBase() { Name = blankName };
            Account.Template = blankTemplate;
            Account.ParentID = parentId;
            InitializeFields();
        }

        /// <summary>
        /// Initializes a new form to edit an account.
        /// </summary>
        /// <param name="account">The account to add/edit.</param>
        public FrmEditAccount(IAccount account)
            :this(){
            this.Account = account;
            editing = true;
            InitializeFields();
        }

        /// <summary>
        /// Sets up databinding and shows the fields.
        /// </summary>
        private void InitializeFields() {
            txtName.Text = Account.Name;
            if(string.IsNullOrEmpty(Account.ParentID)) {
                ddGroup.SelectedIndex = 0;
            } else {
                ddGroup.SelectedValue = Account.ParentID;
            }

            if(editing) {
                bool haveTemplate = false;
                foreach(var template in templates) {
                    if(template.ID == Account.Template.ID) {
                        haveTemplate = true;
                        break;
                    }
                }
                if(!haveTemplate) {
                    Account.Template = Account.DeriveTemplate();
                    bsTemplates.Add(Account.Template);
                }
                ddType.SelectedValue = Account.Template.ID;
                ddType.SelectedIndexChanged += new EventHandler(ddType_SelectedIndexChanged);
            } else {
                ddType.SelectedIndexChanged += new EventHandler(ddType_SelectedIndexChanged);
                //ddType.SelectedItem = Account.Type;
                ddType.SelectedValue = Account.Template.ID;
            }
            ShowFields();
            lstIcons.SmallImageList = WinMobileUtils.TemplateImages;
            lstIcons.LargeImageList = WinMobileUtils.TemplateImages;
            foreach(KeyValuePair<string, int> icon in WinMobileUtils.IconMap) {
                if(icon.Value > 1) { // the first two icons are the open/closed folder images
                    ListViewItem lviImage = new ListViewItem(string.Empty);
                    lviImage.Tag = icon.Key;
                    lviImage.ImageIndex = icon.Value;
                    lstIcons.Items.Add(lviImage);
                    if(icon.Key.Substring(0, icon.Key.Length - 3) == Account.Icon) {
                        lviImage.Selected = true;
                    }
                }
            }
        }

        /// <summary>
        /// Changes the account to the type selected in the ddType ComboBox.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ddType_SelectedIndexChanged(object sender, EventArgs e) {
            string templateId = Convert.ToString(ddType.SelectedValue);
            if(!string.IsNullOrEmpty(templateId)) {
                ITemplate newTemplate = templates.First(t => t.ID == templateId);
                Account.Template = newTemplate;
                Account.Fields = newTemplate.Fields;
                ShowFields();
            }
        }

        /// <summary>
        /// Shows the labels and controls used to edit an account.
        /// </summary>
        private void ShowFields() {
            ddType.SelectedIndexChanged -= new EventHandler(ddType_SelectedIndexChanged);
            textBoxMap.Clear();
            hiddenMap.Clear();
            tbcAccount.TabPages[0].Controls.Clear();
            this.SuspendLayout();
            tbcAccount.TabPages[0].Controls.Add(this.ddType);
            tbcAccount.TabPages[0].Controls.Add(this.ddGroup);
            tbcAccount.TabPages[0].Controls.Add(this.txtName);
            tbcAccount.TabPages[0].Controls.Add(this.lblType);
            tbcAccount.TabPages[0].Controls.Add(this.lblGroup);
            tbcAccount.TabPages[0].Controls.Add(this.lblName);
            int tabIndex = ddType.TabIndex+1;
            int curY = lblType.Top + lblType.Height + fieldSpacingY;
            using(Graphics g = this.CreateGraphics()) {
                fontHeight = (int)Math.Ceiling(g.MeasureString("y", fieldNameFont).Height);
                foreach(IField field in Account.Fields) {
                    string fieldName = field.Name,//.Key.ToString(),
                           fieldValue = field.Value;//.ToString();
                    ContextButton btnContext = null;
                    Label lblFieldName = new Label {
                        Width = labelWidth,
                        Location = new Point(lblName.Left, curY),
                        Font = fieldNameFont,
                        Text = fieldName
                    };
                    tbcAccount.TabPages[0].Controls.Add(lblFieldName);

                    TextBox txtFieldValue = new TextBox();
                    if(FieldBase.GetMetaState(field.Meta, FieldMetaData.None)) {
                        lblFieldName.Text = fieldName;
                        btnContext = new ContextButton(FieldMetaData.None) { Tag = field };
                    }
                    if(FieldBase.GetMetaState(field.Meta, FieldMetaData.Password)) {
                        btnContext = new ContextButton(FieldMetaData.Password) { Tag = field };
                        txtFieldValue.BackColor = Color.LightSalmon;
                    }
                    if(FieldBase.GetMetaState(field.Meta, FieldMetaData.Phone)) {
                        lblFieldName.Text = fieldName.Substring(3);
                        btnContext = new ContextButton(FieldMetaData.Phone) { Tag = field };
                        txtFieldValue.BackColor = Color.LightSkyBlue;
                        /*case '!':
                            hiddenMap.Add(fieldName, fieldValue);
                            continue;*/
                    }
                    if(FieldBase.GetMetaState(field.Meta, FieldMetaData.Url)) {
                        lblFieldName.Text = fieldName.Substring(3);
                        btnContext = new ContextButton(FieldMetaData.Url) {Tag = field};
                    }
                    
                    lblFieldName.Height = Utils.EnlargeLabelToText(g, lblFieldName, fontHeight);
                    btnContext.Location = new Point(btnOptionsX, curY);
                    btnContext.Size = btnOptionsSize;
                    btnContext.ContextItemChecked += delegate(ContextItemCheckedEventArgs e) {
                        SaveFields();
                        var clickedField = btnContext.Tag as IField;
                        if(e.PhoneNumber) {
                            LockCryptBLL.ToggleMetaFlag(clickedField, FieldMetaData.Phone);
                        } else if(e.ObfuscateField) {
                            LockCryptBLL.ToggleMetaFlag(clickedField, FieldMetaData.Password);
                        } else if(e.IsUrl) {
                            LockCryptBLL.ToggleMetaFlag(clickedField, FieldMetaData.Url);
                        } else if(e.RenameField) {
                            using(FrmEditField frmEditField = new FrmEditField(this, Account.Fields, lblFieldName.Text)) {
                                if(frmEditField.ShowDialog() == DialogResult.OK) {
                                    Account.Fields.Remove(clickedField);
                                    clickedField.Name = frmEditField.FieldName;
                                    Account.Fields.Add(clickedField);
                                }
                            }
                        } else if(e.DeleteField) {
                            Account.Fields.Remove(clickedField);
                            //Account.Fields.Remove(fieldName);
                            textBoxMap.Remove(clickedField.Name);
                        }
                        ShowFields();
                    };
                    btnContext.TabIndex = tabIndex;
                    tabIndex++;
                    tbcAccount.TabPages[0].Controls.Add(btnContext);
                    txtFieldValue.Text = fieldValue;
                    txtFieldValue.Location = new Point(txtName.Left, curY);
                    txtFieldValue.Size = txtName.Size;
                    txtFieldValue.TabIndex = tabIndex;
                    tabIndex++;
                    textBoxMap[field.Name] = txtFieldValue;
                    tbcAccount.TabPages[0].Controls.Add(txtFieldValue);

                    curY += lblFieldName.Height > txtFieldValue.Height ?
                                    lblFieldName.Height + fieldSpacingY : txtFieldValue.Height + fieldSpacingY;
                }
                
                Label lblNotes = new Label();
                lblNotes.Size = new Size(labelWidth, txtName.Height);
                lblNotes.Text = I18nUtils.GetString("Strings", "Notes");
                lblNotes.Location = new Point(lblName.Left, curY);
                lblNotes.Font = fieldNameFont;
                txtNotes = new TextBox();
                txtNotes.Text = Account.Notes;
                txtNotes.Multiline = true;
                txtNotes.Size = new Size(txtName.Width, txtName.Height * 2);
                txtNotes.Location = new Point(txtName.Left, curY);

                if(Account.Template.ShowNotes) {
                    tbcAccount.TabPages[0].Controls.Add(lblNotes);
                    tbcAccount.TabPages[0].Controls.Add(txtNotes);
                    curY += txtNotes.Height + fieldSpacingY;
                }
            }
            this.ResumeLayout();
            ddType.SelectedValue = Account.Template.ID;//.Type;
            ddType.SelectedIndexChanged += new EventHandler(ddType_SelectedIndexChanged);
        }

        /// <summary>
        /// Saves the entered values back to the Account object.
        /// </summary>notes
        private void SaveFields() {
            Account.Name = txtName.Text;
            string parent = Convert.ToString(ddGroup.SelectedValue);
            Account.ParentID = string.IsNullOrEmpty(parent) ? null : parent;
            foreach(KeyValuePair<string,TextBox> entry in textBoxMap) {
                Account.Fields.First(f=>f.Name == entry.Key).Value = (entry.Value as TextBox).Text;
            }
            foreach(KeyValuePair<string, string> entry in hiddenMap) {
                Account.Fields.First(f => f.Name == entry.Key).Value = (entry.Value);
            }
            if(Account.Template.ShowNotes) {
                Account.Notes = txtNotes.Text;
            }
            if(lstIcons.SelectedIndices.Count > 0) {
                string iconName = lstIcons.Items[lstIcons.SelectedIndices[0]].Tag.ToString();
                Account.Icon = iconName.Substring(0,iconName.Length-3);
            }
        }

        /// <summary>
        /// Checks that the account name is not null and that no account with the name entered exists. Displays a messagebox if errors are detected.
        /// </summary>
        /// <returns></returns>
        private bool ValidateAccount() {
            if(string.IsNullOrEmpty(txtName.Text)) {
                MessageBox.Show(I18nUtils.GetString("Errors", "AccountNameRequired"), I18nUtils.GetString("Errors", "Error"), MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1);
                txtName.SelectAll();
                txtName.Focus();
                return false;
            }
            return true;
        }
    }
}